import json
from pathlib import Path
from typing import Optional

from metagpt.actions import Action, ActionOutput
from metagpt.actions.write_review_an import (
    DESIGN_REVIEW,
    TASK_REVIEW, 
    GLOBAL_CODE_REVIEW
)
from metagpt.const import DATA_API_DESIGN_FILE_REPO, SEQ_FLOW_FILE_REPO
from metagpt.logs import logger
from metagpt.schema import Document, Documents, Message
import json

DESIGN_REVIEW_TEMPLATE="""
### PRD(Product Requirement Document)
{prd}
### System Design
{system_design}
"""

REQ_REVIEW_TEMPLATE="""
### System Design
{system_design}
### Task List and Dependencies
{task_list}
"""

GLOBAL_CODE_REVIEW_TEMPLATE="""
### System Design
{system_design}
### Task List and Dependencies
{task_list}
### Code Files
{all_code_content}
"""

class WriteReview(Action):
    name: str = ""
    i_context: Optional[str] = None
    desc: str = (
        "Based on next roles result, judge and give an suggestion."
    )

    async def run(self, review_idx:int, with_messages: Message, schema: str = None):
        operfunc = [
            self._system_design_review,
            self._task_review,
            self._global_code_review
        ]
        lenth = len(operfunc)
        if review_idx < 0 or review_idx >= lenth:
            raise Exception(f"error review_idx: {review_idx}, length: {lenth}")
        return await operfunc[review_idx](with_messages,schema)

    async def _global_code_review(self, with_messages: Message, schema: str = None):
        """
        global code review
        """

        # Use `git status` to identify which PRD documents have been modified in the `docs/prd` directory.
        changed_task = self.repo.docs.task.changed_files
        # Use `git status` to identify which design documents in the `docs/system_designs` directory have undergone
        # changes.
        changed_system_designs = self.repo.docs.system_design.changed_files

        # build code content
        changed_code_files_repo = self.repo.with_src_path(self.context.src_workspace)
        src_file_repo = changed_code_files_repo.srcs
        changed_src_files = list(src_file_repo.all_files)
        # concat all code
        codes = []
        for filename in changed_src_files:
            doc = await src_file_repo.get(filename=filename)
            # If the file does not exist in the src workspace, skip it
            if not doc:
                continue
            codes.append(f"----- {filename}\n```{doc.content}```")
        all_code_content = "\n".join(codes)

        # get system_design and task_doc
        old_system_design_doc = None
        task_doc = None
        for filename in changed_system_designs.keys():
            old_system_design_doc = await self.repo.docs.system_design.get(filename)
            task_doc = await self.repo.docs.task.get(filename)
            break

        async def _new_review(llm, system_design_doc, task_doc, all_code_content:str):
            context = GLOBAL_CODE_REVIEW_TEMPLATE.format(
                system_design=system_design_doc.content,
                task_list=task_doc.content,
                all_code_content=all_code_content
            )
            # TODO Action
            review = await GLOBAL_CODE_REVIEW.fill(context=context, llm=llm)
            result = review.instruct_content.model_dump_json()
            return result

        # For those PRDs and design documents that have undergone changes, regenerate the design content.
        result = await _new_review(self.llm, old_system_design_doc, task_doc, all_code_content)
        return result


    async def _system_design_review(self, with_messages: Message, schema: str = None):
        """
        system design review
        """

        # Use `git status` to identify which PRD documents have been modified in the `docs/prd` directory.
        changed_prds = self.repo.docs.prd.changed_files
        # Use `git status` to identify which design documents in the `docs/system_designs` directory have undergone
        # changes.
        changed_system_designs = self.repo.docs.system_design.changed_files

        prd = None
        old_system_design_doc = None
        for filename in changed_system_designs.keys():
            prd = await self.repo.docs.prd.get(filename)
            old_system_design_doc = await self.repo.docs.system_design.get(filename)
            break

        async def _new_review(llm, prd_doc, system_design_doc):
            context = DESIGN_REVIEW_TEMPLATE.format(prd=prd_doc.content, system_design=system_design_doc.content)
            review = await DESIGN_REVIEW.fill(context=context, llm=llm)
            result = review.instruct_content.model_dump_json()
            return result

        # For those PRDs and design documents that have undergone changes, regenerate the design content.
        result = await _new_review(self.llm, prd, old_system_design_doc)
        return result

    async def _task_review(self, with_messages: Message, schema: str = None):
        """
        system design review
        """

        # changes.
        changed_system_designs = self.repo.docs.system_design.changed_files
        changed_tasks = self.repo.docs.task.changed_files

        system_design = None 
        task_list = None

        for filename in changed_system_designs.keys():
            system_design = await self.repo.docs.system_design.get(filename)
            task_list = await self.repo.docs.task.get(filename)
            break

        async def _new_review(llm, system_design_doc, task_list_doc):
            context = REQ_REVIEW_TEMPLATE.format(system_design=system_design_doc.content, task_list=task_list_doc.content)
            review = await TASK_REVIEW.fill(context=context, llm=llm)
            result = review.instruct_content.model_dump_json()
            return result

        # For those PRDs and design documents that have undergone changes, regenerate the design content.
        result = await _new_review(self.llm, system_design, task_list)
        return result

